import time

from detect import CLASSES, set_obj_thresh
from .object_tracer import ObjectTracer
from .triggers import *

class Alerter:

    def __init__(self, *triggers):
        # 区分出两类Trigger，以便在不同的时机触发
        self._obj_enter_triggers = []
        self._obj_leave_triggers = []

        for trigger in triggers:
            if trigger.is_obj_enter_trigger():
                self._obj_enter_triggers.append(trigger)
            if trigger.is_obj_leave_trigger():
                self._obj_leave_triggers.append(trigger)

        # 根据这些集合的并集计算出需要追踪的物体集合
        self._tracing_obj_dict = set().union(*map(lambda trigger: trigger.alert_objs, self._obj_enter_triggers + self._obj_leave_triggers))
        if not self._tracing_obj_dict:
            self._tracing_obj_dict = None
        else:
            # 处理成{类别1: 空集合, 类别2: 空集合...}的格式
            self._tracing_obj_dict = {obj: set() for obj in self._tracing_obj_dict}
        
    def update(self, image, results):
        if not self._tracing_obj_dict:
            return
        
        current_time = time.perf_counter()

        # 获取所有正在追踪的物体，之后排查谁没出现时方便
        notfound_objs = set().union(*self._tracing_obj_dict.values())
        obj_and_action = []
        for box, label_idx, _ in results:
            # 找到对应的类型
            label = CLASSES[label_idx]

            obj_set = self._tracing_obj_dict.get(label)
            if obj_set is None:
                continue

            max_sim = 0
            max_sim_obj = None
            for obj in obj_set:
                # 如果这个物体被认领了就直接跳过
                if obj not in notfound_objs:
                    continue
                sim = obj.sim(box)
                # 找最大位置相似度物体，并且必须大于最小位置相似度
                if sim > max_sim and sim > ObjectTracer.TRACE_MIN_SIM:
                    max_sim = sim
                    max_sim_obj = obj
            if max_sim_obj is not None:
                # 可以找到之前追踪的物体
                notfound_objs.discard(max_sim_obj)
                action = max_sim_obj.update(current_time, box, image)
                if action != ObjectTracer.Action.NONE:
                    obj_and_action.append((max_sim_obj, action))
            else:
                # 那就新建一个物体追踪器
                obj_set.add(ObjectTracer(label, box, image))
        
        for obj in notfound_objs:
            action = obj.update(current_time)
            if action != ObjectTracer.Action.NONE:
                obj_and_action.append((obj, action))
        
        for obj, action in obj_and_action:
            # 判断是否是进入或者离开的物体
            if action == ObjectTracer.Action.ENTER:
                for trigger in self._obj_enter_triggers:
                    if obj.label in trigger.alert_objs:
                        trigger.enter(obj, self._tracing_obj_dict)
            elif action == ObjectTracer.Action.LEAVE:
                for trigger in self._obj_leave_triggers:
                    if obj.label in trigger.alert_objs:
                        trigger.leave(obj, self._tracing_obj_dict)
            
            # 判断该物体还是否需要跟踪
            if action == ObjectTracer.Action.LEAVE or action == ObjectTracer.Action.FAKE:  # FAKE:这个物体是抖动抖出来的，是个假的，不追踪它了
                self._tracing_obj_dict[obj.label].discard(obj)

def linear(min, max, percent):
    return min + (max - min) * percent

def alert_thread_wrapper(
        detect_queue, 
        alert_collect_url, 
        disable_draw_alert_box,
        enter_alert_objs, 
        leave_alert_objs, 
        linkage_alert_objs, 
        pass_alert_objs, 
        pass_alert_directions, 
        gather_alert_objs,
        gather_alert_threshhold,
        alert_sensitivity
    ):
    
    set_alert_collect_url(alert_collect_url)

    alerter = Alerter(
        EnterTrigger(enter_alert_objs),
        LeaveTrigger(leave_alert_objs),
        LinkageEnterTrigger(linkage_alert_objs),
        PassTrigger(pass_alert_objs, pass_alert_directions),
        GatherTrigger(gather_alert_objs, gather_alert_threshhold)
    )

    alert_sensitivity = 1 - alert_sensitivity
    ObjectTracer.TRACE_TIME_THRESHHOLD = linear(0.5, 2, alert_sensitivity)
    ObjectTracer.TRACE_ENTER_PERCENT_THRESHHOLD = linear(0.4, 0.9, alert_sensitivity)
    ObjectTracer.DISABLE_DRAW_ALERT_BOX = disable_draw_alert_box
    set_obj_thresh(linear(0.25, 0.8, alert_sensitivity)) 
    
    while True:
        # results大概长这样：[(<int x1>, <int y1>, <int x2>, <int y2>), <int 类别编号>, <float 置信度>), ...]
        alerter.update(*detect_queue.get())  # frame是opencv的图像，因为报警时需要个base64截图，所以图像也传过来了
